package com.bestv.bsr.core.service.impl.hb;

import cn.hutool.core.collection.CollUtil;
import com.bestv.bsr.core.constants.AiSourceConstants;
import com.bestv.bsr.core.constants.CacheConstants;
import com.bestv.bsr.core.constants.RecommendMethod;
import com.bestv.bsr.core.constants.ServiceCodeEnum;
import com.bestv.bsr.core.context.RecommendContext;
import com.bestv.bsr.core.entities.vo.RecDataVo;
import com.bestv.bsr.core.entities.vo.RecGroupListVo;
import com.bestv.bsr.core.exceptions.ServiceException;
import com.bestv.bsr.core.properties.HBRecommendProperties;
import com.bestv.bsr.core.service.AbstractRecommendEngine;
import com.bestv.bsr.core.service.impl.hb.remote.HBRemoteService;
import com.google.common.base.Splitter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author taojiacheng
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class HBRecommendEngine extends AbstractRecommendEngine {

    private final HBRecommendProperties properties;
    private final HBRemoteService hbRemoteService;

    protected static final int MAX_RESULT = 50;
    protected static final String CATE_NAME = "catename";
    protected static final String USER_ID = "userid";
    protected static final String VIDEO_ID = "videoid";

    @Override
    public RecDataVo recommend(RecommendContext recommendContext) {
        String methodType = recommendContext.getMethodType();
        RecommendMethod recommendMethod = RecommendMethod.fromCode(methodType);
        String resp;
        String itemType = recommendContext.getItemTypes().get(0);
        String itemCode = CollectionUtils.isEmpty(recommendContext.getItemCodes()) ? "" : recommendContext.getItemCodes().get(0);
        String cacheKey = "";
        switch (recommendMethod) {
            case COLLABORATIVE_FILTERING:
                cacheKey = CacheConstants.format(CacheConstants.HB_CACHE,
                        recommendContext.getThirdSceneId(), recommendContext.getUserAccount(), itemType, "");
                Map<String, String> guessParams = buildGuessParams(recommendContext);
                resp = hbRemoteService.recommend(guessParams, "guess", cacheKey);
                resp = handlerResponseStr(resp);
                break;
            case ITEM_BASE:
                cacheKey = CacheConstants.format(CacheConstants.HB_CACHE,
                        recommendContext.getThirdSceneId(), recommendContext.getUserAccount(), itemType, itemCode);
                Map<String, String> relativeParams = buildRelativeParams(recommendContext);
                resp = hbRemoteService.recommend(relativeParams, "relative", cacheKey);
                resp = handlerResponseStr(resp);
                break;
            default:
                throw new ServiceException(ServiceCodeEnum.METHODTYPE_NOT_SUPPORTED);
        }
        return convertResult(resp, recommendContext);
    }

    protected RecDataVo convertResult(String resp, RecommendContext recommendContext) {
        List<RecGroupListVo> groupList = new ArrayList<>();
        int size = recommendContext.getSize();
        String itemType = recommendContext.getItemTypes().get(0);
        // 拼装结果对象
        RecDataVo recDataVo = new RecDataVo();
        List<String> resultItemCodes = parseRespVideos(resp, size);
        RecGroupListVo recGroupListVo = new RecGroupListVo(itemType, resultItemCodes);
        groupList.add(recGroupListVo);
        recDataVo.setGroupList(groupList);
        recDataVo.setRecResultId(RandomStringUtils.randomAlphanumeric(8));
        return recDataVo;
    }


    /**
     * 获取返回结果中的节目编码列表
     */
    public List<String> parseRespVideos(String respVideos, int size) {
        List<String> videoIdList = new ArrayList<>();
        try {
            if (StringUtils.isNotBlank(respVideos)) {
                videoIdList = Splitter.on(",").omitEmptyStrings().trimResults().splitToList(respVideos);
            }
        } catch (Exception e) {
            log.error("推荐id {} 湖北第三方推荐接口返回异常", respVideos);
            throw new ServiceException(ServiceCodeEnum.THIRD_PARTY_RESULT_INVALID);
        }
        if (CollectionUtils.isEmpty(videoIdList)) {
            log.warn("湖北第三方推荐接口返回为空，respVideo{}", respVideos);
        }
        if (size < videoIdList.size()) {
            videoIdList = videoIdList.subList(0, size);
        }
        return videoIdList;
    }

    /**
     * 构建 "猜你喜欢" 接口参数
     */
    protected Map<String, String> buildGuessParams(RecommendContext recommendContext) {
        Map<String, String> param = new HashMap<>();
        String userAccount = recommendContext.getUserAccount();
        String sceneId = recommendContext.getSceneId();
        String cateName = recommendContext.getItemTypes().get(0);
        // param设置userid
        initParams(sceneId, userAccount, cateName, param, true);
        return param;
    }


    /**
     * 构建"相关推荐"接口参数
     */
    private Map<String, String> buildRelativeParams(RecommendContext recommendContext) {
        String sceneId = recommendContext.getSceneId();
        String videoId = recommendContext.getItemCodes().get(0);
        String cateName = recommendContext.getItemTypes().get(0);
        Map<String, String> param = new HashMap<>();
        initParams(sceneId, videoId, cateName, param, false);
        return param;
    }


    protected void initParams(String scene, String value, String cateName, Map<String, String> param, boolean isGuess) {
        param.put("n", Integer.toString(MAX_RESULT));
        param.put("scene", scene);
        param.put(isGuess ? USER_ID : VIDEO_ID, value);
        param.put(CATE_NAME, "99");
        Map<String, String> itemTypeToCateNameMap = properties.getItemTypeMap();
        String type = itemTypeToCateNameMap.get(cateName);
        if (StringUtils.isNotEmpty(type)) {
            param.put(CATE_NAME, type.substring(0, type.indexOf(":")));
        }
    }

    protected String handlerResponseStr(String responseStr) {
        if (responseStr.contains("|")) {
            return responseStr.substring(responseStr.indexOf("|") + 1);
        } else {
            log.error("HB_NUO_YOU第三方推荐接口返回不合法，resp:{}", responseStr);
            throw new ServiceException(ServiceCodeEnum.THIRD_PARTY_RESULT_INVALID);
        }
    }

    @Override
    public void init(RecommendContext recommendContext) {
        Map<String, String> sceneConvertMap = properties.getSceneConvertMap();
        String sceneId = recommendContext.getSceneId();
        String convertSceneId = sceneConvertMap.get(sceneId);
        if (StringUtils.isEmpty(convertSceneId)) {
            log.error("湖北第三方推荐接口不支持的sceneId: {}", sceneId);
            throw new ServiceException(ServiceCodeEnum.SCENE_ID_NOT_SUPPORTED);
        }
        recommendContext.setThirdSceneId(convertSceneId);
    }

    @Override
    public void validate(RecommendContext recommendContext) {
        List<String> itemTypes = recommendContext.getItemTypes();
        Map<String, String> itemTypeMap = properties.getItemTypeMap();
        List<String> intersection = itemTypes.stream().filter(itemTypeMap::containsKey).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(intersection)) {
            return;
        }
        log.error("湖北第三方推荐接口不支持的itemType: {}", CollUtil.join(itemTypes, ","));
        throw new ServiceException(ServiceCodeEnum.ITEM_TYPES_NOT_SUPPORTED);
    }

    @Override
    protected List<String> addSupportedAiSource() {
        return Collections.singletonList(AiSourceConstants.AI_SOURCE_HBYD_NUOYOU);
    }
}
